﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace SkyBase
{

    class World
    {
        //WorldMap for prototype, contains individual gameObjects
        //I'm using a string to object dictionary because we will
        //have many objects of the same type in different locations
        //e.g. walls
        //This will allow us to still access objects individually
        //while having control over "special" objects like the player
        //or enemies.
        //(the world class is currently on hold until it becomes needed)
        private Dictionary<string, GameObject> world;
        //Stores the locations of every object in the game world.
        private Dictionary<string, Vector2> pointMap;

        public bool levelLoading = false; //if true, do not update or draw until new level is loaded

        public Player player;
        Line jumpline;
        private Game1 game;
        public Texture2D playerSprite;
        public int Floor { get; set; }
        public int size_x;
        public int size_y;

        //LevelGen values
        const int TILE_ENEMY = -5;
        const int TILE_HP = -3;
        const int TILE_ITEM = -2;
        const int TILE_STAIRS = -1;
        const int TILE_FLOOR = 0;
        const int TILE_WALL = 1;
        const int TILE_SKY = 2;
        const int TILE_CLIFF = 3;

        //private readonly int MAX_ENEMIES = 10;
        //private readonly int MIN_ENEMIES = 2;
        
        //Lists for synchronous adding and removing of objects.
        public List<string> toRemove;
        public Dictionary<String, GameObject> toAdd;

        public Random random;

        public bool isPlayerAlive = true;

        //Are we in the middle of our update loop?
        private bool midUpdate = false;

        //Shall we switch to the next level?
        public bool levelUp = false;

        public World(Game1 gameIn)

        {
            //Debugger.enabled = true;
            game = gameIn;
            playerSprite = Storage.getTexture("Character");
            world = new Dictionary<string, GameObject>();
            pointMap = new Dictionary<string, Vector2>();
            Floor = 1;
            jumpline = new Line("jumpLine", Storage.getTexture("line"), false, this);
            jumpline.ghost = true;
            jumpline.showingUp = false;
            jumpline.checkForCollision = true;
            LoadObject(jumpline);
            random = new Random();
            //populateMap();
        }

        public void Initialize()
        {
            Debugger.debug("Initializing world.");
            player = new Player("player", this, playerSprite);
            LoadObject(player);
            populateMap();

            //createEnemies();

            //Put the player on the first "floor" we can find.
            spawnPlayer();
            Debugger.debug("Finished initializing world.");
             
        }

        /// <summary>
        /// Puts the player at first floor tile we can find
        /// </summary>
        public void spawnPlayer()
        {
            player.Location = GetObject("floor").Location + new Vector2(player.Bounds.Width / 2, player.Bounds.Height / 2);
            levelLoading = false;
        }

        /// <summary>
        /// Gets called everytime the game itself updates. The World keeps track of all the items and updates everything in this method.
        /// </summary>
        public void Update(GameTime gameTime)
        {
            if (!levelLoading)
            {
                GameObject obj;
                Line l = (Line)objects["jumpLine"];
                l.lineColor = Color.White;
                //Objects to remove after this passthrough.
                toRemove = new List<string>();
                //Ditto for objects to add
                toAdd = new Dictionary<string, GameObject>();

                midUpdate = true;
                foreach (KeyValuePair<string, GameObject> pair in world)
                {
                    obj = pair.Value;
                    if (obj is Enemy)
                        (obj as Enemy).updateDirection();

                    //Test collisions here
                    bool lockPlayerX = false;
                    bool lockPlayerY = false;
                    //Temporary if statement to make sure only the player
                    //is checked for collisions
                    if (obj.checkForCollision)
                    {
                        GameObject obj2;
                        Rectangle bounds = obj.Bounds;

                        Rectangle xPBounds;
                        Rectangle yPBounds;

                        if (obj.VX > 0)
                            xPBounds = new Rectangle(bounds.X + (int)Storage.clamp(obj.VX, obj.VX, 1) + (int)Storage.clamp(obj.VX, 5, -5), bounds.Y, bounds.Width / 2, bounds.Height / 2);
                        else
                            if(obj.VX < 0)
                                xPBounds = new Rectangle(bounds.X + (int)Storage.clamp(obj.VX, -1, obj.VX) + (int)Storage.clamp(obj.VX, 5, -5), bounds.Y, bounds.Width / 2, bounds.Height / 2);
                            else//if vx == 0
                                xPBounds = new Rectangle(bounds.X, bounds.Y, bounds.Width / 2, bounds.Height / 2);

                        if (obj.VY > 0)
                            yPBounds = new Rectangle(bounds.X, bounds.Y + (int)Storage.clamp(obj.VY, obj.VY, 1) + (int)Storage.clamp(obj.VY, 5, -5), bounds.Width / 2, bounds.Height / 2);
                        else
                            if(obj.VY < 0)
                                yPBounds = new Rectangle(bounds.X, bounds.Y + (int)Storage.clamp(obj.VY, -1, obj.VY) + (int)Storage.clamp(obj.VY, 5, -5), bounds.Width / 2, bounds.Height / 2);
                            else//if vy == 0
                                yPBounds = new Rectangle(bounds.X, bounds.Y, bounds.Width / 2, bounds.Height / 2);
                        try
                        {
                            foreach (KeyValuePair<string, GameObject> pair2 in world)
                            {
                                obj2 = pair2.Value;
                                //If not colliding with self and collidee is collidable 
                                if (!(obj2.Equals(obj)) && obj2.isCollidable)
                                {
                                    //Collision on X axis
                                    if (xPBounds.Intersects(obj2.Bounds))
                                    {
                                        //player.VX = 0;
                                        Debugger.writeLine(obj + " colliding with " + obj2.Name);
                                        if (!obj.ghost)
                                            lockPlayerX = true;
                                    }

                                    //Collision on Y axis
                                    if (yPBounds.Intersects(obj2.Bounds))
                                    {
                                        //player.VY = 0;
                                        Debugger.writeLine(obj.Name + " colliding with " + obj2.Name);
                                        if (!obj.ghost)
                                            lockPlayerY = true;
                                    }
;

                                    //Bad Derin, bad
                                    if ((obj.ghost && (xPBounds.Intersects(obj2.Bounds)) || ((yPBounds.Intersects(obj2.Bounds))
                                        || (lockPlayerX || lockPlayerY))))
                                        obj.onCollision(obj2);


                                }
                            }
                        }
                        catch (InvalidOperationException)
                        {
                            break;
                        }



                        if (!lockPlayerX)
                            //obj.Location += new Vector2(obj.VX, 0);
                            obj.updateLocation(0, -obj.VY);

                        if (!lockPlayerY)
                            //obj.Location += new Vector2(0, obj.VY);
                            obj.updateLocation(-obj.VX, 0);

                        if (obj is Enemy)
                        {
                            obj.updateTextures("enemy");
                            (obj as Enemy).rotated = false;
                            if (Vector2.Distance(obj.Location, this.GetObject("player").Location) < 200)
                            {
                                if ((obj as Enemy).canFire())
                                {
                                    enemyFireBullet(obj as Enemy);
                                    (obj as Enemy).fired();
                                }
                            }
                        }

                        if (obj is Bullet && (outOfBounds(obj.Location) || (obj as Bullet).removeMe))
                            toRemove.Add(pair.Key);

                    }
                    else
                        obj.updateLocation();

                }

                midUpdate = false;

                foreach (KeyValuePair<string, GameObject> x in toAdd)
                    LoadObject(x.Value, x.Key);

                foreach (string key in toRemove)
                    removeObject(key);

                player.Update();

            }

            if (levelUp)
            {
                clearLevel();
                Floor++;
                populateMap();
                spawnPlayer();
                levelUp = false;
            }
        }


        public bool outOfBounds(Vector2 location)
        {
            if (location.X > 5000 ||
                location.X < -5000 ||
                location.Y > 5000 ||
                location.Y < -5000)
                return true;
            return false;

        }

        public void Draw(SpriteBatch spriteBatch)
        {
            if (!levelLoading)
            {
                // spriteBatch.Draw(playerSprite, player.location, Color.Black);
                GameObject tempObj;
                try
                {
                    spriteBatch.Draw(Storage.getTexture("BackgroundLarge"),
                        GetObject("player").Location - new Vector2(GetEngine().viewportSize.Width / 2, GetEngine().viewportSize.Height / 2),
                        null,
                        Color.White,
                        0,
                        new Vector2(0, 0),
                        1f,
                        SpriteEffects.None,
                        0.6f);

                    //Draw objects
                    foreach (KeyValuePair<string, GameObject> pair in world)
                    {
                        tempObj = pair.Value;

                        if (tempObj.showingUp)
                        {
                            tempObj.Draw(spriteBatch);
                            if (tempObj.drawBounds)
                            {
                                
                                Texture2D tex = new Texture2D(GetEngine().graphics.GraphicsDevice, tempObj.Bounds.Width, tempObj.Bounds.Height);
                                Color[] red = new Color[tempObj.Bounds.Width * tempObj.Bounds.Height];
                                for (int i = 0; i < tempObj.Bounds.Width * tempObj.Bounds.Height; i++)
                                    red[i] = Color.Blue;
                                tex.SetData<Color>(red);
                                spriteBatch.Draw(tex, tempObj.bounds,
                                     null, Color.White, (float)tempObj.GetRotation(),
                                     new Vector2(0, 0), SpriteEffects.None, 0);
                            }
                        }



                    }

                    Player player = GetObject("player") as Player;
                    if (player.drawBounds)
                    {
                        Texture2D tex = new Texture2D(GetEngine().graphics.GraphicsDevice, player.Bounds.Width, player.Bounds.Height);
                        Color[] red = new Color[player.Bounds.Width * player.Bounds.Height];
                        for (int i = 0; i < player.Bounds.Width * player.Bounds.Height; i++)
                            red[i] = Color.Red * 0.8f;
                        tex.SetData<Color>(red);

                        Rectangle xPBounds;
                        Rectangle yPBounds;


                        if (player.VX > 0)
                            xPBounds = new Rectangle(player.Bounds.X + (int)Storage.clamp(player.VX, player.VX, 1) + (int)Storage.clamp(player.VX, 5, -5), player.Bounds.Y, player.Bounds.Width / 2, player.Bounds.Height / 2);
                        else
                            xPBounds = new Rectangle(player.Bounds.X + (int)Storage.clamp(player.VX, -1, player.VX) + (int)Storage.clamp(player.VX, 5, -5), player.Bounds.Y, player.Bounds.Width / 2, player.Bounds.Height / 2);

                        if (player.VY > 0)
                            yPBounds = new Rectangle(player.Bounds.X, player.Bounds.Y + (int)Storage.clamp(player.VY, player.VY, 1) + (int)Storage.clamp(player.VX, 5, -5), player.Bounds.Width / 2, player.Bounds.Height / 2);
                        else
                            yPBounds = new Rectangle(player.Bounds.X, player.Bounds.Y + (int)Storage.clamp(player.VY, -1, player.VY) + (int)Storage.clamp(player.VX, 5, -5), player.Bounds.Width / 2, player.Bounds.Height / 2);


                        spriteBatch.Draw(tex, xPBounds,
                             null, Color.White, (float)player.GetRotation(),
                             new Vector2(0, 0), SpriteEffects.None, 0);

                        spriteBatch.Draw(tex, yPBounds,
                             null, Color.White, (float)player.GetRotation(),
                             new Vector2(0, 0), SpriteEffects.None, 0);
                    }
                    spriteBatch.Draw(player.weaponSprite, player.weaponLocation, null, Color.White * 1f, (float)player.weaponRotation - (float)(Math.PI * (float)(3f/16f)), 
                        new Vector2(player.weaponSprite.Bounds.Center.X, player.weaponSprite.Bounds.Center.Y + 50), SpriteEffects.None, 0);



                }
                catch { }

            }
        }



        /// <summary>
        /// Generates the map.
        /// </summary>
        public void populateMap()
        {
            //Debugger.enabled = true;
            Vector2 centerLoc = new Vector2((GetEngine().viewportSize.Width/2),(GetEngine().viewportSize.Height/2));
            Debugger.debug("Populating map...");
            
            LevelGen generator = new LevelGen(random);

            size_x = random.Next(60, 60 + (Floor * 2));
            size_y = random.Next(45, 45 + (Floor * 2));
            int[,] map = generator.CreateLevel(size_x, size_y, 45, 5, 2, 5, 4);
            Vector2 drawSpot = new Vector2(GetEngine().viewportSize.Width / -2, GetEngine().viewportSize.Height / -2);
            int nameCt = 0;
            for (int y = 0; y < size_y; y++)
            {
                for (int x = 0; x < size_x; x++)
                {
                    int r = random.Next(1, 3);
                    if (map[x, y] == TILE_FLOOR)
                    {LoadObject(new GameObject("floor", drawSpot, Storage.getTexture("Floor"+r), false, this));
                        }
                    else if (map[x, y] == TILE_WALL)
                        LoadObject(new GameObject("wall", drawSpot, Storage.getTexture("Wall"+r), true, this, 0.4f));
                    else if (map[x, y] == TILE_STAIRS)
                        LoadObject(new Stairs(drawSpot, this));
                    else if (map[x, y] == TILE_ITEM)
                    {
                        LoadObject(new GameObject("floor", drawSpot, Storage.getTexture("Floor2"), false, this));
                        Weapon tempW = Weapon.GenRandomWeapon(Floor, random, this);
                        tempW.Location = drawSpot;
                        LoadObject(tempW);
                    }
                    else if (map[x, y] == TILE_ENEMY)
                    {
                        LoadObject(new GameObject("floor", drawSpot, Storage.getTexture("Floor2"), false, this));
                        Enemy enemy = new Enemy("enemy" + nameCt, this, random, Storage.getTexture("Robot"));
                        //enemy.color = Color.Red;
                        enemy.Location = drawSpot;
                        LoadObject(enemy);
                        nameCt++;
                    }
                    else if (map[x, y] == TILE_CLIFF)
                    {
                        drawCliffs(map, drawSpot, x, y);
                    }
                    else if (map[x,y] == TILE_HP)
                        LoadObject(new HealthTile(drawSpot, this, random));
                    drawSpot.X += 64;
                    //Debugger.writeLine("x is " + x);
                }
                drawSpot.X = GetEngine().viewportSize.Width / -2;
                drawSpot.Y +=64;
                //Debugger.writeLine("y is " + y);
            }

            Debugger.debug("Finished populating map.");
        }

        /// <summary>
        /// Get the absolute location of an object
        /// </summary>
        /// <param name="objectName"></param>
        /// <returns></returns>
        public Vector2 GetLocation(string objectName)
        {
            return pointMap[objectName];
        }

        /// <summary>
        /// Get an object.
        /// </summary>
        /// <param name="objectName"></param>
        /// <returns></returns>
        public GameObject GetObject(string objectName)
        {
            return world[objectName];
        }

        public void removeObject(string objectName)
        {
            Debugger.enabled = true;
            Debugger.writeLine("Removing " + objectName);
            world.Remove(objectName);
            Debugger.writeLine(objectName + " removed from world.");
            pointMap.Remove(objectName);
            Debugger.writeLine(objectName + " removed from pointMap.");
            Debugger.enabled = false;
        }

        /// <summary>
        /// Load an object into the world.
        /// If name is not provided, object name will be used.
        /// If duplicates exist a number will be appended to
        /// the end of the object's name and the operation
        /// will continue
        /// </summary>
        /// <param name="toLoad">The GameObject to load.</param>
        /// <param name="name">The optional name (key) to use</param>
        public void LoadObject(GameObject toLoad, string name = null)
        {
            Debugger.writeLine("Adding object [" + toLoad.Name + "] with specified name [" + name + "]");
            if (name == null)
            {

                Debugger.writeLine("No name specified, using GameObject name: [" + toLoad.Name +"]");

                if (world.ContainsKey(toLoad.Name))
                {
                    Debugger.writeLine("Duplicate entry for key [" + toLoad.Name + "] found.");
                    int num;
                    bool isNumber;

                    if (world[toLoad.Name].Name.Length >= 4)
                        isNumber = int.TryParse(world[toLoad.Name].Name.Substring(world[toLoad.Name].Name.Length - 4, 4), out num);
                    else
                        isNumber = false;

                    if (isNumber)
                    {
                        num = Convert.ToInt32(world[toLoad.Name].Name.Substring(world[toLoad.Name].Name.Length - 4, 4));
                        num++;
                    }
                    else
                    {
                        num = 0001;
                    }

                    name = toLoad.Name + num.ToString("0000") ;

                    while (world.ContainsKey(name))
                    {
                        //Debugger.writeLine("Another duplicate has been found. Number: " + name.Substring(name.Length - 3, 3));
                        num = Convert.ToInt32(name.Substring(name.Length - 4, 4));
                        num++;
                        name = toLoad.Name + num.ToString("0000");
                        toLoad.duplicateNum = num;
                    }
                    Debugger.writeLine("Appending duplicate number: " + num.ToString("0000"));
                    Debugger.writeLine("Using '" + name + "' as name.");
                }
                else
                    name = toLoad.Name;
                
            }

            if (!midUpdate)
            {
                world.Add(name, toLoad);
                pointMap.Add(name, toLoad.Location);
                Debugger.writeLine("Added object [" + name + "]");
            }
            else
            {
                if(!toAdd.ContainsKey(name) && !toAdd.ContainsValue(toLoad))
                    toAdd.Add(name, toLoad);
            }

        }

        /// <summary>
        /// Get the engine.
        /// </summary>
        /// <returns>The Game1 instance.</returns>
        public Game1 GetEngine()
        {
            return game;
        }

        /// <summary>
        /// Get the dictionary storing all in-game objects.
        /// </summary>
        public Dictionary<string, GameObject> objects
        {
            get { return world; }
        }

        /// <summary>
        /// Get the dictionary storing all in-game object locations.
        /// </summary>
        public Dictionary<string, Vector2> objectLocations
        {
            get { return pointMap; }
        }

        public void playerFireBullet()
        {
            double vX = Math.Cos((player.weaponRotation + Math.PI / 2 - (Math.PI/6)))*8.0;
            double vY = Math.Sin((player.weaponRotation + Math.PI / 2 -(Math.PI / 6)))*8.0;
            
            Bullet bullet = new Bullet(new Vector2(player.locationRec.Center.X-15-(float)(vX*5) , player.locationRec.Center.Y-15-(float)(vY*5) ), Storage.getTexture("bullet"), this, player.equippedWeapon.BaseDamage, player);

            bullet.drawBounds = true;
            bullet.SetRotation(player.weaponRotation + Math.PI / 2 - (Math.PI/6));

            bullet.VX = -(float)vX;
            bullet.VY = -(float)vY;
            Debugger.writeLine("Firing bullet!");
            LoadObject(bullet);
        }

        public void enemyFireBullet(Enemy enemy)
        {
            Rectangle locationRec = new Rectangle((int)enemy.Location.X, (int)enemy.Location.Y, 64, 64);
            Vector2 weaponSlope = new Vector2(locationRec.X /*+ -GetEngine().GetCameraPos().X)*/ - player.locationRec.Center.X, locationRec.Y /*+ -GetEngine().GetCameraPos().Y)*/ - player.locationRec.Center.Y);
            double actualweaponSlope = weaponSlope.Y / weaponSlope.X;
            double weaponRotation = (Math.Atan(actualweaponSlope) /*+ (Math.PI / 2) + (Math.PI / 6)*/);
            if ((weaponSlope.Y > 0 && weaponSlope.X < 0) || (weaponSlope.Y < 0 && weaponSlope.X < 0)) weaponRotation += Math.PI;
            double vX = Math.Cos((weaponRotation /*+ Math.PI / 2  - (Math.PI / 6)*/)) * 5.0;
            double vY = Math.Sin((weaponRotation/* + Math.PI / 2 /* - (Math.PI / 6)*/)) * 5.0;

            //Math.Atan2(world.GetObject("player").Location.Y - Location.Y, world.GetObject("player").Location.X - Location.X)

            Bullet bullet = new Bullet(new Vector2(locationRec.Center.X - 15 - (float)(vX * 5), locationRec.Center.Y - 15 - (float)(vY * 5)), Storage.getTexture("bullet"), this, Floor, enemy);

            bullet.SetRotation(weaponRotation /*+ Math.PI / 2 - (Math.PI / 6)*/);

            bullet.VX = -(float)vX;
            bullet.VY = -(float)vY;
            
            LoadObject(bullet);
        }

        public void clearLevel()
        {
            levelLoading = true;
            Player reservePlayer = (Player)world["player"];
            Line reserveLine = (Line)world["jumpLine"];
            reservePlayer.VX = 0;
            reservePlayer.VY = 0;
            world.Clear();
            pointMap.Clear();
            toAdd.Clear();
            toRemove.Clear();
            LoadObject(reservePlayer);
            LoadObject(reserveLine);
            /*foreach (string key in world.Keys)
            {
                if (key != "player")
                    removeObject(key);
            }*/
        }

        public void drawCliffs(int[,] map, Vector2 drawSpot, int x, int y)
        {
            if (y == 0)
            {
                if (x == 0)
                {
                    if ((map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_se"), true, this));
                    else if ((map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_east"), true, this));
                    else if ((map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_south"), true, this));
                }
                else if (x == size_x - 1)
                {
                    if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_sw"), true, this));
                    else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_west"), true, this));
                    else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_south"), true, this));
                }
                else
                {
                    if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff3_south"), true, this));
                    else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ew"), true, this));
                    else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_se"), true, this));
                    else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_sw"), true, this));
                    else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_west"), true, this));
                    else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_south"), true, this));
                    else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_east"), true, this));
                }
            }

            else if (y == size_y - 1)
            {
                if (x == 0)
                {
                    if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ne"), true, this));
                    else if ((map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_east"), true, this));
                    else if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_north"), true, this));
                }
                else if (x == size_x - 1)
                {
                    if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_nw"), true, this));
                    else if ((map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_west"), true, this));
                    else if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_north"), true, this));
                }
                else
                {
                    if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff3_north"), true, this));
                    else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ne"), true, this));
                    else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_nw"), true, this));
                    else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ew"), true, this));
                    else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_north"), true, this));
                    else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_east"), true, this));
                    else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF))
                        LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_west"), true, this));
                }
            }
            else if (x == 0 && y > 0 && y < size_y-1)
            {
                if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff3_east"), true, this));
                else if ((map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_se"), true, this));
                else if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ne"), true, this));
                else if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ns"), true, this));
                else if ((map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_east"), true, this));
                else if ((map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_south"), true, this));
                else if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_north"), true, this));
            }
            else if (x == size_x - 1 && y > 0 && y < size_y - 1)
            {
                if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP) && (map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff3_west"), true, this));
                else if ((map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP) && (map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_sw"), true, this));
                else if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF) && (map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_nw"), true, this));
                else if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP) && (map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ns"), true, this));
                else if ((map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF) && (map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_west"), true, this));
                else if ((map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP) && (map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_south"), true, this));
                else if ((map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF) && (map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_north"), true, this));
            }
            else
            {
                if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff4"), true, this));
                else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff3_east"), true, this));
                else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff3_west"), true, this));
                else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff3_south"), true, this));
                else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff3_north"), true, this));

                else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ns"), true, this));
                else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_se"), true, this));
                else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ne"), true, this));
                else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_sw"), true, this));
                else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_nw"), true, this));
                else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff2_ew"), true, this));

                else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_FLOOR || map[x, y + 1] == TILE_WALL || map[x, y + 1] == TILE_ENEMY || map[x, y + 1] == TILE_ITEM || map[x, y + 1] == TILE_STAIRS || map[x, y + 1] == TILE_HP))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_south"), true, this));
                else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_FLOOR || map[x + 1, y] == TILE_WALL || map[x + 1, y] == TILE_ENEMY || map[x + 1, y] == TILE_ITEM || map[x + 1, y] == TILE_STAIRS || map[x + 1, y] == TILE_HP) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_east"), true, this));
                else if ((map[x - 1, y] == TILE_SKY || map[x - 1, y] == TILE_CLIFF) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_FLOOR || map[x, y - 1] == TILE_WALL || map[x, y - 1] == TILE_ENEMY || map[x, y - 1] == TILE_ITEM || map[x, y - 1] == TILE_STAIRS || map[x, y - 1] == TILE_HP) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_north"), true, this));
                else if ((map[x - 1, y] == TILE_FLOOR || map[x - 1, y] == TILE_WALL || map[x - 1, y] == TILE_ENEMY || map[x - 1, y] == TILE_ITEM || map[x - 1, y] == TILE_STAIRS || map[x - 1, y] == TILE_HP) && (map[x + 1, y] == TILE_SKY || map[x + 1, y] == TILE_CLIFF) && (map[x, y - 1] == TILE_SKY || map[x, y - 1] == TILE_CLIFF) && (map[x, y + 1] == TILE_SKY || map[x, y + 1] == TILE_CLIFF))
                    LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_west"), true, this));
            }
            //LoadObject(new GameObject("cliff", drawSpot, Storage.getTexture("cliff1_south"), true, this));
        }
    }
}
